package org.lgiavedoni.android.franq.service.prediction;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;

import org.lgiavedoni.android.franq.core.prediction.Prediction;
import org.lgiavedoni.android.franq.core.prediction.PredictionTimerTask;
import org.lgiavedoni.android.franq.core.prediction.dao.PredictionDAO;
import org.lgiavedoni.android.franq.core.prediction.imp.PredictionEventDate;
import org.lgiavedoni.android.franq.core.prediction.rule.PredictionRule;
import org.lgiavedoni.android.franq.core.prediction.rule.dao.PredictionRuleDAO;
import org.lgiavedoni.android.franq.core.prediction.strategy.PredictionStrategy;
import org.lgiavedoni.android.franq.core.service.Service;
import org.lgiavedoni.android.franq.service.log.Logger;
import org.lgiavedoni.android.franq.util.date.DateUtil;
import org.lgiavedoni.android.franq.util.timer.TimerUtil;

public class PredictionService extends Service {

	public static final PredictionService inst = new PredictionService();
	private final PredictionDAO predictionDAO = PredictionDAO.inst;
	private final PredictionRuleDAO predictionRuleDAO = PredictionRuleDAO.inst;
	private Timer predictionTimer;
	private final List<PredictionStrategy> strategies = new ArrayList<PredictionStrategy>();
	private static final String IMPLEMENTATIONS_KEY = "predictionStrategies";

	private PredictionService() {
	}

	@Override
	public String getServiceName() {
		return "prediction";
	}

	@Override
	protected void startInternal() {
		predictionTimer = new Timer();
	}

	@Override
	protected void stopInternal() {
		if (predictionTimer != null) {
			predictionTimer.cancel();
			predictionTimer = null;
		}
		// TODO should I remove all or only remove the timer task?
		// predictionDAO.deleteAll();

	}

	public void addPrediction(Prediction p) {
		if (p instanceof PredictionEventDate) {
			if (((PredictionEventDate) p).getPredictedDate().getTime() < DateUtil.getTimeInMillis()) {
				// Old prediction
				return;
			}
			PredictionTimerTask timerTask = new PredictionTimerTask((PredictionEventDate) p);
			TimerUtil.addPrediction(predictionTimer, timerTask);
		}
		if (!p.isSaved())
			predictionDAO.add(p);
	}

	@Override
	public void _run() {
		recreateTimer();
		for (Object obj : getImplementations(IMPLEMENTATIONS_KEY)) {
			PredictionStrategy ps = (PredictionStrategy) obj;
			strategies.add(ps);
		}
		for (Prediction p : PredictionDAO.inst.getAll()) {
			addPrediction(p);
		}

	}

	public void generatePredictions() {
		Logger.inst.info("PredictionService: predictions generation STARTED");

		// First remove all old predictions
		predictionDAO.deleteAll();
		recreateTimer();

		for (PredictionRule pr : predictionRuleDAO.getAll()) {
			for (Prediction p : pr.generatePredictions()) {
				addPrediction(p);
			}
		}
		Logger.inst.info("PredictionService: predictions generation FINISHED");
	}

	public void updatePredictionRules() {
		Logger.inst.info("PredictionService: prediction rules generation STARTED");
		predictionRuleDAO.deleteAllNoValidatedForUser();

		for (PredictionStrategy ps : strategies) {
			Logger.inst.info("PredictionService: running " + ps.getClass().getSimpleName() + " strategy");
			predictionRuleDAO.add(ps.generateNewRules());
			ps.updatePredictionRuleOcurrences();
		}
		Logger.inst.info("PredictionService: prediction rules generation FINISHED");
	}

	private void recreateTimer() {
		if (predictionTimer != null)
			predictionTimer.cancel();
		predictionTimer = new Timer();
	}

}